#!/usr/bin/env python
#
# Handy dandy library on how to do simple crypto in Python.
#
# Copyright 2017 kzimmermann <https://quitter.se/kzimmermann>
#
# This program is Free Software licensed under the terms and conditions of
# the GNU GPL v3. See https://gnu.org/licenses
#

'''
Portable "password-generation library" in python to generate strong or
reusable passwords.

Can be used as a module or as a standalone command-line program as long as 
getpass() is available.
'''

import hashlib
import os
import sys
import binascii
import base64 as b64

from getpass import getpass

def helper():
    print'''
Strong password generator in python.
Warning: prints password plaintext to stdout!

USAGE: passgen.py [OPTION], where:
    -h, --help: prints this help message
    -r, --random: prints out a random password
    -k, --key: stretches the password into a key instead (only nonrandom)
    -l [length], --length [length]: prints out a password of [length]-chars
    '''

def generate(salt, string, length=32):
    '''
    Generate a strong, yet deterministic password by making use of a common
    "salt" as a password seed and a service-specific string.

    Passwords are by default 32-char long, but should not be issued to less
    than 14 characters (80 bits entropy) due to the ease of brute-forcing.
    '''
    return b64.b64encode(hashlib.sha224(salt + string).hexdigest())[0:length]

def passgen(length = 32, is_key = False):
    '''
    Practical application of the password generator functions.
    '''
    salt = getpass("Enter a salt: ")
    string = raw_input("Enter a service-specific string: ")

    if is_key:
        print "Your password is: %s" % keygen(salt, string, length)
    else:
        print "Your password is: %s" % generate(salt, string, length)

def randpass(bits = 24):
    '''
    Returns a string composed of random bytes represented as a string, which
    is suitable for use as a password (though it does nothing to store it).

    Default is 24, which produces a 32-char string. You shouldn't use less
    than 9 bits, though.
    '''
    return binascii.b2a_hqx(os.urandom(bits))

def keygen(string, salt, length = 32):
    '''
    Generates a strong, virtually unguessable key for a password by using
    a key stretching algorithm (PBKDF2) with a random salt. The type is str.

    By default, 24bits of salt are used.
    '''
    return binascii.b2a_hqx(
        hashlib.pbkdf2_hmac('sha256', string, salt, 200000)
    )[0:length]

if __name__ == "__main__":
    def shift():
        "Practical implementation of the Unix shift command"
        sys.argv.reverse()
        sys.argv.pop()
        sys.argv.reverse()

    is_random = False
    is_key = False
    bits = 24
    length = 32
    shift()

    while len(sys.argv) > 0:
        arg = sys.argv[0]
        if arg == "-r" or arg == "--random":
            is_random = True
        elif arg == "-k" or arg == "--key":
            is_key = True
        elif arg == "-l" or arg == "--length":
            shift()
            try:
                if is_random:
                    bits = int(sys.argv[0])
                else:
                    length = int(sys.argv[0])
            except IndexError:
                print "Error: please specify a length"
                sys.exit(1)
        elif arg == "-h" or arg == "--help":
            helper()
            sys.exit(0)
        else:
            print "Error: unknown option '%s'" % arg
            sys.exit(1)

        # call next round...
        shift()
        
    if is_random:
        print randpass(bits)
        sys.exit(0)

    passgen(length, is_key)
